/*-----------------------------------------------------------------------------
		   Licensed to the Apache Software Foundation (ASF) under one
           or more contributor license agreements.  See the NOTICE file
           distributed with this work for additional information
           regarding copyright ownership.  The ASF licenses this file
           to you under the Apache License, Version 2.0 (the
           "License"); you may not use this file except in compliance
           with the License.  You may obtain a copy of the License at

             http://www.apache.org/licenses/LICENSE-2.0

           Unless required by applicable law or agreed to in writing,
           software distributed under the License is distributed on an
           "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
           KIND, either express or implied.  See the License for the
           specific language governing permissions and limitations
           under the License.  
-----------------------------------------------------------------------------*/

package parser;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
import data.CurrentTask;
import data.SFile;

import languages.C;

/**
 * This class will normalize the file's source code by applying the appropriate
 * techniques to remove comments and special syntax that is not needed. The
 * language type of the file will dictate how these will be removed.
 * 
 * @author Kevin.J.Jalbert
 */
public class Normalizer implements Runnable{

	// Get instance of CurrentTask
	private static CurrentTask currentTask = CurrentTask.getInstance();
	public String threadName = null;
	public final int BUFFER_SIZE = 127; // Buffer size to increase speed

	/**
	 * Instantiates a new normalizer.
	 * 
	 * @param threadName the thread name
	 */
	public Normalizer(String threadName) {
		this.threadName = threadName;
	}

	/**
	 * This thread will look for files on the normalization queue in the
	 * currentTask object. If there is no files available it will have a timeout
	 * check to ensure that the queue is indeed empty. When a file is acquired
	 * the contents are read and the normalization process occurs.
	 */
	@Override
	public void run() {

		int timeout = 10;

		// Check to make sure the queue isn't empty
		while (timeout != 0){

			// Have a thread delay to allow the queue to refill
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}

			while(!currentTask.isNormQueueEmpty()){
				timeout = 10; // Reset the timeout counter

				File workingFile = currentTask.getFileFromNormQueue();

				// Check to make sure the File is valid
				if (workingFile != null){
					readContent(workingFile);
				}
			}
			timeout--;
		}
	}

	/**
	 * Read the contents of the working file, and places it in a stringbuffer.
	 * 
	 * @param workingFile the working file
	 */
	private void readContent(File workingFile){

		// Read in the workingFile
		FileReader fileReader = null;
		try {
			fileReader = new FileReader(workingFile);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		BufferedReader bufReader = new BufferedReader(fileReader);
		StringBuffer fileContents = new StringBuffer();

		// If bufferReader is ready start parsing it, and appending to the string
		String line = "";
		try {
			if(bufReader.ready()){
				String buffer = "";

				// Keep reading till done
				while((line = bufReader.readLine()) != null){

					buffer = buffer + line + "\n";

					// If buffer is overflowed then dump it into fileContents
					if(buffer.length() > this.BUFFER_SIZE){
						fileContents.append(buffer);
						buffer = "";
					}
					line = null;
				}
				fileContents.append(buffer);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}

		// Remove the comments for this file
		removeComments(workingFile, fileContents);
	}

	/**
	 * Removes the comments from the file's content using a buffer approach
	 * along with the complimented language object.
	 * 
	 * @param workingFile the working file
	 * @param fileContents the file contents
	 */
	private void removeComments(File workingFile, StringBuffer fileContents) {

		String code = fileContents.toString();
		String uncommentedCode = "";

		// TODO Need to figure out the file extension and use that language
		C lang = new C();

		// CharacterIterator that will read one character at a time till done
		CharacterIterator charIter = new StringCharacterIterator(code);
		while(charIter.current() != CharacterIterator.DONE){

			// Get the current character and add it the buffer
			int currentPosition = charIter.getIndex();
			String character = Character.toString(charIter.current());
			lang.addToBuffer(character);

			// If the lang's internal buffer matched something
			if (lang.characterMatch()){

				// If the match is not a comment
				if(lang.isCommentStart()){

					// Get the position of when the buffer for this comment started
					int bufferStart = currentPosition - lang.getRemovedCharacters()-1; 
					charIter.setIndex(bufferStart);
					String buffer = "\n";

					// Write out the buffer
					for (int i = bufferStart; i < currentPosition-1; i++){
						buffer = buffer + Character.toString(charIter.current());
						charIter.next();
						if(buffer.length() == this.BUFFER_SIZE){
							uncommentedCode = uncommentedCode.concat(buffer);
							buffer = "";
						}
					}

					// Append the buffer
					uncommentedCode = uncommentedCode.concat(buffer);
				}
				// If the match is a comment just flush the buffer
				else if(lang.isCommentEnd()){
					lang.getRemovedCharacters();
				}
			}
			charIter.setIndex(currentPosition+1);
		}

		// Get the position of when the buffer for this comment started
		int currentPosition = charIter.getEndIndex();
		int bufferStart = currentPosition - lang.getRemovedCharacters();
		charIter.setIndex(bufferStart);
		String buffer = "";

		// Write out the buffer
		for (int i = bufferStart; i < currentPosition; i++){
			buffer = buffer + Character.toString(charIter.current());
			charIter.next();
			if(buffer.length() == this.BUFFER_SIZE){
				uncommentedCode = uncommentedCode.concat(buffer);
				buffer = "";
			}
		}

		// Append the buffer
		uncommentedCode = uncommentedCode.concat(buffer);

		// Remove the extra pieces of syntax that is not needed
		String cleanCode = removeExtras(uncommentedCode);

		// Make a new SFile object for this file
		SFile currentFile = new SFile(workingFile, code.length(), uncommentedCode.length(), cleanCode);

		// Pass this file to the Cyclolizer Queue
		currentTask.addFileToCycloQueue(currentFile);

		code = null;
		uncommentedCode = null;
		fileContents = null;
	}

	/**
	 * Remove the extra pieces of syntax that are not needed.
	 * 
	 * @param uncommentedCode the uncommented code
	 * 
	 * @return cleanCode the clean code
	 */
	private String removeExtras(String uncommentedCode) {
		String cleanCode = uncommentedCode.replaceAll("[\n]+", "\n"); // Empty Lines
		cleanCode = cleanCode.replaceAll("[\"].*?[\"]", ""); // Double Quotes
		cleanCode = cleanCode.replaceAll("[{][\'|\"|\\w].*?[\'|\"|\\w][}]", ""); // Array settings
		cleanCode = cleanCode.replaceAll("[\'].*?[\']", ""); // Single Quotes
		cleanCode = cleanCode.replaceAll("[#][\\t|\\w]*.*?[\\s]",""); // Includes
		return cleanCode;
	}
}